#pragma region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU Lesser General Public License.
/// </copyright>
#pragma endregion

#pragma once

class Timer
{
private:
	class CallBackInfo
	{
	public:
		virtual void Call() {}
	};

	template<class T>
	class CallBackInfoT : public CallBackInfo
	{
	private:
		typedef void (T::* MethodT)(Timer&);
		
		Timer& _timer;
		T* _obj;
		MethodT _method;

	public:
		CallBackInfoT<T>(Timer& timer, T* obj, MethodT method)
			: _timer(timer), _obj(obj), _method(method)
		{
		}

		virtual void Call()
		{
			(_obj->*_method)(_timer);
		}
	};

	UINT_PTR _timerId;

	static ATL::CCriticalSection& GetCriticalSection()
	{
		static ATL::CCriticalSection eventMapCritSection;
		return eventMapCritSection;
	}

	static ATL::CSimpleMap<UINT_PTR, CallBackInfo*>& GetEventMap()
	{
		static ATL::CSimpleMap<UINT_PTR, CallBackInfo*> eventMap;
		return eventMap;
	}

public:
	
	Timer() : _timerId(NULL)
	{
	}

	~Timer()
	{
		KillTimer();
	}

	bool Enabled() { return _timerId != NULL; }

	template<class T>
	HRESULT SetTimer(T* obj, void (T::* method)(Timer&), UINT timeout)
	{
		KillTimer();

		ATL::CCritSecLock lock(GetCriticalSection());
		UINT_PTR timerId = ::SetTimer(NULL, 0, timeout, TimerCallBack);
		if(timerId == NULL)
			return AtlHresultFromLastError();

		CallBackInfo* callBackInfo = new CallBackInfoT<T>(*this, obj, method);
		if(callBackInfo == NULL)
			return E_OUTOFMEMORY;

		if(!GetEventMap().Add(timerId, callBackInfo))
		{
			delete callBackInfo;
			return E_OUTOFMEMORY;
		}

		_timerId = timerId;
		return S_OK;
	}

	HRESULT KillTimer()
	{
		if(_timerId == NULL)
			return S_OK;

		ATL::CCritSecLock lock(GetCriticalSection());

		int index = GetEventMap().FindKey(_timerId);
		if(index == -1)
			return S_OK;

		CallBackInfo* callBackInfo = GetEventMap().GetValueAt(index);
		delete callBackInfo;

		GetEventMap().RemoveAt(index);
		
		::KillTimer(NULL, _timerId);
		_timerId = NULL;

		return S_OK;
	}


	static void CALLBACK TimerCallBack(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
	{
		ATL::CCritSecLock lock(GetCriticalSection());
		
		int index = GetEventMap().FindKey(idEvent);
		if(index == -1)
			return;

		CallBackInfo* callBackInfo = GetEventMap().GetValueAt(index);

		lock.Unlock();

		callBackInfo->Call();
	}

};

class Utils
{
public:
	static bool GetFilePathFromHandle(HANDLE hFile, CString& filePath);
	static HRESULT SaveBitmapFile(LPCWSTR pszFile, const BITMAPINFO* bitmapInfo);
};